package com.czh.mock.result;

import com.czh.mock.exceptions.FileException;
import com.czh.mock.exceptions.code.MockCodeEnum;
import com.czh.mock.result.config.ResultConfig;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public abstract class AbstractMockResult {
    final Logger logger = LoggerFactory.getLogger(AbstractMockResult.class);
    protected ResultConfig config;
    /**
     * 字段和对应mock算法
     */
    private List<FieldSegment<Object, Object>> fieldSegmentList = new ArrayList<>();
    /**
     * 记录生成的每条mock数据
     */
    private List<List<MockFieldSegment>> mockRowList = new ArrayList<>();
    /**
     * 写入文件的输出流
     */
    private OutputStream outputStream;

    /**
     * 获取保存的文件目录
     *
     * @return 保存的文件目录
     */
    abstract String getPath();

    /**
     * 获取文件后缀
     *
     * @return 文件后缀
     */
    abstract String getFileSuffix();

    /**
     * 将生成的mock数据批量写入文件
     *
     * @param mockRowList 生成的mock数据
     * @param close       是否关闭输出流
     * @return 写入的字节数
     */
    abstract void write(List<List<MockFieldSegment>> mockRowList, boolean close);
    /**
     * 获取mock数据的输出成品路径
     *
     * @return 输出成品路径
     */
    public abstract String  getOutputResultPath();

    /**
     * 定义mock的字段
     *
     * @param field         字段名
     * @param param         mock算法的参数
     * @param mockAlgorithm mock算法
     * @param <T>           param的类型
     * @param <R>           mock算法的返回值
     * @return this
     */
    public <T, R> AbstractMockResult mockField(String field, T param, Function<T, R> mockAlgorithm) {
        FieldSegment<T, R> fieldSegment = new FieldSegment<>(field, param, mockAlgorithm);
        this.getFieldSegmentList().add((FieldSegment<Object, Object>) fieldSegment);
        return this;
    }

    /**
     * 执行mock数据和写入文件的流程
     *
     * @return this
     */
    public final AbstractMockResult execute() {
        for (int i = 0; i < this.getConfig().getCount(); i++) {
            // 批量写入文件按每批500条执行
            mockRowList.add(this.mockRow());
            if (mockRowList.size() >= this.getConfig().getBatchCount()) {
                // 写入文件
                this.write(this.mockRowList, false);
                // 清空集合
                mockRowList.clear();
            }
        }
        if (!mockRowList.isEmpty()) {
            this.write(this.mockRowList, true);
            mockRowList.clear();
        }
        return this;
    }

    public List<FieldSegment<Object, Object>> getFieldSegmentList() {
        return fieldSegmentList;
    }

    public ResultConfig getConfig() {
        return config;
    }

    public List<List<MockFieldSegment>> getMockRowList() {
        return mockRowList;
    }

    public OutputStream getOutputStream(boolean forceNew) {
        if (this.outputStream == null || forceNew) {
            this.outputStream = this.getFileStream();
        }
        return outputStream;
    }

    /**
     * 关闭文件输出流
     *
     * @throws IOException
     */
    protected final void closeStream() throws IOException {
        if (this.outputStream != null) {
            this.outputStream.close();
        }
    }

    /**
     * 根据设置的mock参数生成一条数据
     *
     * @return mock后的一条数据
     */
    protected List<MockFieldSegment> mockRow() {
        List<MockFieldSegment> fieldInRow = new ArrayList<>();
        for (int j = 0; j < this.getFieldSegmentList().size(); j++) {
            FieldSegment<Object, Object> record = this.getFieldSegmentList().get(j);
            Function<Object, Object> function = record.getMockAlgorithm();
            Object rs = function.apply(record.getParam());
            fieldInRow.add(new MockFieldSegment(record.getColumn(), rs));
        }
        return fieldInRow;
    }

    /**
     * 根据设置的mock参数提取列名
     *
     * @return SQL语句格式的列名字符串
     */
    protected final String extractFields() {
        String fields = fieldSegmentList.stream().map(FieldSegment::getColumn).collect(Collectors.joining("`,`"));
        fields = "`" + fields + "`";
        return fields;
    }

    /**
     * 根据文件名获取文件输出流
     *
     * @return 文件输出流
     */
    private FileOutputStream getFileStream() {
        this.getConfig().setAbsoluteFilename(this.getPath() + this.getConfig().getFilename() + this.getFileSuffix());
        File file = new File(this.getConfig().getAbsoluteFilename());
        if (!this.getConfig().isOverwrite() && file.exists()) {
            throw new FileException(MockCodeEnum.FILE_EXISTS, "文件已存在：" + this.getConfig().getAbsoluteFilename());
        }
        FileOutputStream outputStream;
        try {
            // 如果目录不存在，则递归创建目录
            if (!file.getParentFile().exists()) {
                Validate.isTrue(file.getParentFile().mkdirs(), "目录创建失败");
            }
            outputStream = new FileOutputStream(file, !this.getConfig().isOverwrite());
        } catch (FileNotFoundException e) {
            logger.error("文件创建失败", e);
            throw new FileException(MockCodeEnum.FILE_CREATED_ERROR);
        }
        return outputStream;
    }
}
